﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Anagramme.Model;
using System.ApplicationModel;
using System.ComponentModel.Composition;
using Anagramme.Contracts;
using System.Windows.Input;
using Anagramme.Views;
using System.ApplicationModel.Composition;
using System.Windows;
using System.ComponentModel.Composition.Hosting;
using System.Collections.ObjectModel;
using Anagramme.Services;
using System.Activities;
using Anagramme.Activities.BusinessRules;

namespace Anagramme.ViewModels
{
    /// <summary>
    /// ViewModel de la partie
    /// </summary>
    [ViewModelExport(Modules.Game)]
    [PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
    public class GameViewModel : ViewModelBase<IGameModel>
    {
        #region Constructors
        /// <summary>
        /// Crée une nouvelle instance de la classe <see cref="GameViewModel"/>
        /// </summary>
        public GameViewModel()
        {
            IGameModel model = base.Model;

            WeakEventManager<IGameModel, EventArgs>.AddHandler(model, "OnResponseGameDisconnect", this.Model_OnResponseGameDisconnect);
            WeakEventManager<IGameModel, EventArgs>.AddHandler(model, "OnResponseGameFinished", this.Model_OnResponseGameFinished);
            WeakEventManager<IGameModel, EventArgs<int, int>>.AddHandler(model, "OnResponseGameIncreaseTeamScore", this.Model_OnResponseGameIncreaseTeamScore);
            WeakEventManager<IGameModel, EventArgs<RoundInfo>>.AddHandler(model, "OnResponseGameNewRound", this.Model_OnResponseGameNewRound);
            WeakEventManager<IGameModel, EventArgs<int>>.AddHandler(model, "OnResponseGamePlayerLeave", this.Model_OnResponseGamePlayerLeaved);
            WeakEventManager<IGameModel, EventArgs>.AddHandler(model, "OnResponseGameRoundFinished", this.Model_OnResponseGameRoundFinished);
            WeakEventManager<IGameModel, EventArgs<int>>.AddHandler(model, "OnResponseGameWordFound", this.Model_OnResponseGameWordFound);
            WeakEventManager<IGameModel, EventArgs<int, bool>>.AddHandler(model, "OnResponseGameWordSubmit", this.Model_OnResponseGameWordSubmit);

            this._myWords = new ObservableCollection<WordInfo>();
            this._opponentWords = new ObservableCollection<WordInfo>();

            base.BeginWaitingAction();
            WeakEventManager<IGameModel, EventArgs<GameInfo>>.AddHandler(model, "OnResponseGameInfos", this.Model_OnResponseGameInfos);
            model.RequestGameInfos();

            base.BeginWaitingAction();
            WeakEventManager<IGameModel, EventArgs<Team>>.AddHandler(model, "OnResponseGameRedTeamInfos", this.Model_OnResponseGameRedTeamInfos);
            model.RequestGameRedTeamInfos();

            base.BeginWaitingAction();
            WeakEventManager<IGameModel, EventArgs<Team>>.AddHandler(model, "OnResponseGameBlueTeamInfos", this.Model_OnResponseGameBlueTeamInfos);
            model.RequestGameBlueTeamInfos();
        }
        #endregion

        #region Fields
        private bool _isRed;

        private Team _myTeam;
        private Team _opponentTeam;

        private ObservableCollection<WordInfo> _myWords;
        private ObservableCollection<WordInfo> _opponentWords;

        private GameInfo _game;
        private RoundInfo _round;

        private ICommand _submitWordCommand;
        private ICommand _quitCommand;
        #endregion

        #region Public Properties
        /// <summary>
        /// Obtient l'équipe du joueur
        /// </summary>
        public Team MyTeam
        {
            get { return this._myTeam; }
            private set
            { 
                if (!object.ReferenceEquals(this._myTeam, value))
                {
                    base.RaisePropertyChanging();
                    this._myTeam = value;
                    base.TraceAssign(value);
                    base.RaisePropertyChanged();
                }
            }
        }

        /// <summary>
        /// Obtient l'équipe adverse
        /// </summary>
        public Team OpponentTeam
        {
            get { return this._opponentTeam; }
            private set
            {
                if (!object.ReferenceEquals(this._opponentTeam, value))
                {
                    base.RaisePropertyChanging();
                    this._opponentTeam = value;
                    base.RaisePropertyChanged();
                }
            }
        }

        /// <summary>
        /// Obtient la liste des mots du joueur
        /// </summary>
        public ObservableCollection<WordInfo> MyWords
        {
            get { return this._myWords; }
        }

        /// <summary>
        /// Obtient la liste des mots de l'équipe adverse
        /// </summary>
        public ObservableCollection<WordInfo> OpponentWords
        {
            get { return this._opponentWords; }
        }

        /// <summary>
        /// Obtient les informations sur la partie
        /// </summary>
        public GameInfo Game
        {
            get { return this._game; }
            private set
            {
                if (!object.ReferenceEquals(this._game, value))
                {
                    base.RaisePropertyChanging();
                    this._game = value;
                    base.RaisePropertyChanged();
                }
            }
        }

        /// <summary>
        /// Obtient les informations sur le round courant
        /// </summary>
        public RoundInfo Round
        {
            get { return this._round; }
            private set
            {
                if (!object.ReferenceEquals(this._round, value))
                {
                    base.RaisePropertyChanging();
                    this._round = value;
                    base.RaisePropertyChanged();
                }
            }
        }
        #endregion

        #region Commands
        /// <summary>
        /// Obtient la commande pour soumettre un mot
        /// </summary>
        public ICommand SubmitWordCommand
        {
            get
            {
                if (object.ReferenceEquals(this._submitWordCommand, null))
                {
                    this._submitWordCommand = new DelegateCommand<int>(this.SubmitWord, this.CanSubmitWord);
                }
                return this._submitWordCommand;
            }
        }

        /// <summary>
        /// Obtient la commande pour quitter la partie
        /// </summary>
        public ICommand QuitCommand
        {
            get
            {
                if (object.ReferenceEquals(this._quitCommand, null))
                {
                    this._quitCommand = new DelegateCommand(this.Quit, this.CanQuit);
                }
                return this._quitCommand;
            }
        }
        #endregion

        #region Model Callbacks
        private void Model_OnResponseGameBlueTeamInfos(object sender, EventArgs<Team> e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            int teamId = e.Value.Id;
            ApplicationServices.Trace.TraceData("teamId", teamId);

            WeakEventManager<IGameModel, EventArgs<Team>>.RemoveHandler(this.Model, "OnResponseGameBlueTeamInfos", this.Model_OnResponseGameBlueTeamInfos);
            if (Player.Team.Id == teamId)
            {
                this.MyTeam = e.Value;
            }
            else
            {
                this.OpponentTeam = e.Value;
            }
            base.EndWaitingAction();

            if (!base.IsBusy)
            {
                base.Model.RequestGameLoaded();
            }

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private void Model_OnResponseGameDisconnect(object sender, EventArgs e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            if (!base.Player.IsLobbyMaster)
            {
                ApplicationServices.Message.ShowInformation(Resources.LobbyStrings.CONNECTION_LOST);
            }
            ApplicationServices.ViewWorkflow.SendMessage(Messages.GameGohome);

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private void Model_OnResponseGameFinished(object sender, EventArgs e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            ApplicationServices.ViewWorkflow.SendMessage(Messages.GameFinished);

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private void Model_OnResponseGameIncreaseTeamScore(object sender, EventArgs<int, int> e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            int teamId = e.Value;
            int teamScore = e.Value2;
            ApplicationServices.Trace.TraceData("teamId", teamId);
            ApplicationServices.Trace.TraceData("teamScore", teamScore);

            if (this.MyTeam.Id == teamId)
            {
                this.MyTeam.Score = teamScore;
            }
            else if (this.OpponentTeam.Id == teamId)
            {
                this.OpponentTeam.Score = teamScore;
            }

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private void Model_OnResponseGameInfos(object sender, EventArgs<GameInfo> e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            WeakEventManager<IGameModel, EventArgs<GameInfo>>.RemoveHandler(this.Model, "OnResponseGameInfos", this.Model_OnResponseGameInfos);
            this.Game = e.Value;
            base.EndWaitingAction();

            if (!base.IsBusy)
            {
                base.Model.RequestGameLoaded();
            }

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private void Model_OnResponseGameNewRound(object sender, EventArgs<RoundInfo> e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            this.Round = e.Value;

            Collection<WordInfo> mywords;
            Collection<WordInfo> opponentWords;

            if (this._isRed)
            {
                mywords = this.Round.RedWords;
                opponentWords = this.Round.BlueWords;
            }
            else
            {
                mywords = this.Round.BlueWords;
                opponentWords = this.Round.RedWords;
            }

            this.MyWords.Clear();
            foreach (var word in mywords)
            {
                this.MyWords.Add(word);
            }

            this.OpponentWords.Clear();
            foreach (var word in opponentWords)
            {
                this.OpponentWords.Add(word);
            }

            this.Game.CurrentRound++;

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private void Model_OnResponseGamePlayerLeaved(object sender, EventArgs<int> e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            int playerId = e.Value;
            ApplicationServices.Trace.TraceData("playerId", playerId);

            if (!(base.Player.Id == playerId))
            {
                ApplicationServices.Message.ShowInformation(Resources.LobbyStrings.CONNECTION_LOST);
            }
            ApplicationServices.ViewWorkflow.SendMessage(Messages.GameGohome);

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private void Model_OnResponseGameRedTeamInfos(object sender, EventArgs<Team> e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            WeakEventManager<IGameModel, EventArgs<Team>>.RemoveHandler(this.Model, "OnResponseGameRedTeamInfos", this.Model_OnResponseGameRedTeamInfos);
            if (Player.Team.Id.Equals(e.Value.Id))
            {
                this.MyTeam = e.Value;
                this._isRed = true;
            }
            else
            {
                this.OpponentTeam = e.Value;
                this._isRed = false;
            }
            base.EndWaitingAction();

            if (!base.IsBusy)
            {
                base.Model.RequestGameLoaded();
            }

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private void Model_OnResponseGameRoundFinished(object sender, EventArgs e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private void Model_OnResponseGameWordFound(object sender, EventArgs<int> e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            int wordIndex = e.Value;
            ApplicationServices.Trace.TraceData("wordIndex", wordIndex);

            this.MyWords[wordIndex].IsFound = true;

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private void Model_OnResponseGameWordSubmit(object sender, EventArgs<int, bool> e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            int wordIndex = e.Value;
            bool isWordFound = e.Value2;
            ApplicationServices.Trace.TraceData("wordIndex", wordIndex);
            ApplicationServices.Trace.TraceData("isWordFound", isWordFound);

            this.OpponentWords[wordIndex].IsFound = isWordFound;
            if (!isWordFound)
            {
                this.OpponentWords[wordIndex].Value = string.Empty;
            }
            base.EndWaitingAction();

            ApplicationServices.Trace.TraceMethodLeaving();
        }
        #endregion

        #region Methods
        private bool CanSubmitWord(int wordNumber)
        {
            ApplicationServices.Trace.TraceMethodEntering();
            ApplicationServices.Trace.TraceData("wordNumber", wordNumber);

            Dictionary<string, object> arguments = new Dictionary<string, object>()
            {
                { "IsBusy", base.IsBusy }
            };

            bool returnValue = (bool)WorkflowInvoker.Invoke(new NotBusyActivity(), arguments)["ReturnValue"];

            ApplicationServices.Trace.TraceReturnValue(returnValue);
            ApplicationServices.Trace.TraceMethodLeaving();
            return returnValue;
        }
        private void SubmitWord(int wordNumber)
        {
            ApplicationServices.Trace.TraceMethodEntering();
            ApplicationServices.Trace.TraceData("wordNumber", wordNumber); 

            string word = this.OpponentWords[wordNumber].Value;
            base.BeginWaitingAction();
            System.Threading.Thread.Sleep(200);
            base.Model.RequestGameSubmitWord(wordNumber, word);

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private bool CanQuit()
        {
            ApplicationServices.Trace.TraceMethodEntering();

            Dictionary<string, object> arguments = new Dictionary<string, object>()
            {
                { "IsBusy", base.IsBusy }
            };

            bool returnValue = (bool)WorkflowInvoker.Invoke(new NotBusyActivity(), arguments)["ReturnValue"];

            ApplicationServices.Trace.TraceReturnValue(returnValue); 
            ApplicationServices.Trace.TraceMethodLeaving();
            return returnValue;
        }
        private void Quit()
        {
            ApplicationServices.Trace.TraceMethodEntering();

            bool isLobbyMaster = base.Player.IsLobbyMaster;
            ApplicationServices.Trace.TraceReturnValue(isLobbyMaster);

            if (isLobbyMaster)
            {
                base.Model.RequestGameClose();
            }
            else
            {
                base.Model.RequestGameLeave();
            }

            ApplicationServices.ViewWorkflow.SendMessage(Messages.GameGohome);

            ApplicationServices.Trace.TraceMethodLeaving();
        }
        #endregion
    }
}
